/*
 * start.s: Monahans-P start code using XLLI
 *
 * Copyright (C) 2005 Intel Corporation (stanley.cai@intel.com)
 *
 * (C) Copyright 2006 Marvell International Ltd.
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
#define C_SYMBOL(symbol)    _##symbol
#define MFP_I2C_SCL 0x40e102c4
#define MFP_I2C_SDA 0x40e102c8
.text

/*
 * We start by implementing *all* exception vectors
 *
 * Reset vector: this initialises the machine
 * note here that this not yet taken sleep wakeup into account -- lets just
 * get something to work first.
 */
.macro blink, count
	mov  r3, \count
	b    endless_blink
.endm

.macro cpwait reg
	mrc  p15,0,\reg,c2,c0,0
	mov  \reg,\reg
	sub  pc,pc,#4
.endm

.macro wait, count
	mov		r0, \count
0:
	subs		r0, r0, #1
	bne		0b
.endm

.section .text.init
.globl _mainCRTStartup
_mainCRTStartup:
	b	reset
	b	undefined_instruction
	b	software_interrupt
	b	prefetch_abort
	b	data_abort
	b	not_used
	b	irq
	b	fiq

reset:
2:
	/* setup SP pointer */
	/* put it at the end of iSRAM */
	ldr		sp, =0x5c040000

        /* set CPU to SVC32 mode */
        mrs             r0, cpsr
        bic             r0, r0, #0x1f
        orr             r0, r0, #0x13
        msr             cpsr, r0

        /* turn everything off (caches off, MMU off, etc.) */
        mov             r0, #0x78
        mcr             p15, 0, r0, c1, c0, 0

	/* PSR, is the processor in S2/S3 state
	ldr	r1, =0x40f50004
	ldr	r0, [r1]
	tst     r0, #1
	bne     init_volt*/

	/* Initialize GPIO */
	bl 	xlli_GPIO_init
	bl	xlli_MFPR_init

	/* Hack to enable the LCD backlight */
	bl	init_i2c

	/* LCD backlight full intensity */
	mov	r4, #0x68	/* Device address */
	mov	r5, #0x3c	/* Register offset */
	mov	r6, #0x7f	/* Data */
	bl	writei2c

	/* First write operation need twice */
	mov	r4, #0x68	/* Device address */
	mov	r5, #0x3c	/* Register offset */
	mov	r6, #0x7f	/* Data */
	bl	writei2c

	/* LCD power 23mA */
	mov	r4, #0x68	/* Device address */
	mov	r5, #0x3d	/* Register offset */
	mov	r6, #0x35	/* Data */
	bl	writei2c

init_volt:
	/* init I2C */
	bl      init_i2c

	/* VCC_CORE = 1375mv VCC_SRAM = 1400mv*/
	mov     r4, #0x68 /* Device address */
	mov     r5, #0x10 /* (OVER1) Register offset */
	mov     r6, #0x05 /* Data */
	bl      writei2c  /* I2C warm up in case the first write would fail */

	mov     r4, #0x68 /* Device address */
	mov     r5, #0x24 /* (ADTV2) Register offset */

	/* If is MHN LV A1, we raise core voltage to
	 * 1500mv to workaround NAND DB error.
	 */
	mrc     p15, 0, r2, c0, c0, 0
	mov	r3, #0xFF
	orr	r3, r3, #0xFF00
	and     r2, r2, r3

	mov	r8, #0x91
	orr	r8, r8, #0x6800
	cmp	r2, r8

	movne   r6, #0x1A /* Not MHN LV A1, 1375mv */
	moveq	r6, #0x1F /* MHN LV A1, 1500mv */
  bl      writei2c

	mov     r4, #0x68 /* Device address */
	mov     r5, #0x2A /* (SDTV2) Register offset */
	mov     r6, #0x1B /* Data */
	bl      writei2c

	mov     r4, #0x68 /* Device address */
	mov     r5, #0x20 /* (VCC1) Register offset */
	mov     r6, #0x33 /* Data */
	bl      writei2c

	mov     r4, #0x68 /* Device address */
	mov     r5, #0x0B /* Sysctl register for Watchdog */
	mov     r6, #0x79 /* Data */
	bl      writei2c

	mov     r4, #0x68 /* Device address */
	mov     r5, #0x15 /* LDO10 for TECH_3P */
	mov     r6, #0x02 /* Data */
	bl      writei2c

	mov     r4, #0x68 /* Device address */
	mov     r5, #0x16 /* LDO10 for TECH_3P */
	mov     r6, #0x02 /* Data */
	bl      writei2c

	mov     r4, #0x68 /* Device address */
	mov     r5, #0x11 /* OVER2 */
	mov     r6, #0xFB /* Enable LDO13, 12, 11, 10, 9, 7 & 6 */
	bl      writei2c

	/* Initialize LEDs */
	bl	ledsetup
	bl	led1set
	bl 	led2set

  /* Initialize Interrupt Controller */
  /* Step 1 - Enable CP6 permission */
  mrc     p15, 0, r1, c15, c1, 0  @ read CPAR
  orr     r1, r1, #0x40
  mcr     p15, 0, r1, c15, c1, 0
  cpwait  r1

  bl      xlli_intr_init

init_clock:
	/*
         * turn off most of D0CKEN_A and D0CKEN_B clocks by default, except
         * SMC, DMC, SRAM, I2C, GPIO,interrupt controller
         */
	ldr     r4, =0x41340000
	ldr     r0, =0x00000700
        /*
         * According to the DM,  for CKENA, Write 0b1 to reserve bit.
         */
        orr     r0, r0, #0x82000000
        orr     r0, r0, #0x00090000
        orr     r0, r0, #0x00000001
	str     r0, [r4, #0x0C]
	ldr     r0, [r4, #0x0C]

	ldr     r0, =0xe00000d0
        /*
         * According to the DM, for CKENB, Write 0b1 to reserve bit.
         */
        orr     r0, r0, #0xFF000000
        orr     r0, r0, #0x00FC0000
        orr     r0, r0, #0x0000FE00
        orr     r0, r0, #0x0000002C
	str     r0, [r4, #0x10]
	ldr     r0, [r4, #0x10]

	/* change frequency routine */
	ldr	r4, =0x41340000
	@  .... ..10 1... 11.. 1011 .010 ...0 0100
	@ldr	r0, =0x00033108		@ 104MHz (Run)
	@ldr	r0, =0x01073110		@ 208MHz (Run)
	@ldr	r0, =0x010b7210		@ 416MHz (Turbo)
	@ldr	r0, =0x028fb218		@ 624MHz (Turbo)
	@ldr	r0, =0x028fb21f		@ 806MHz (Turbo)
	ldr	r0, =0x028fb218		@ 624MHz (Turbo)
	str	r0, [r4, #0x00]
        @orr     r2,  r2,  #2
        @mcr     p14, 0, r2, c6, c0, 0

	bl	xlli_freq_change

    ldr	r1, bss_start
	ldr	r0, bss_end
	sub	r0, r0, r1
	/* r1 = start address */
	/* r0 = *number of bytes */
	mov	r2, #0
	mov r3, #0
	mov	r4, #0
	mov	r5, #0

clear_bss:
	stmia	r1!, {r2,r3,r4,r5}
	subs	r0, r0, #16
	bne	clear_bss
  bl init_memory
  bl _c_main

.globl undefined_instruction
undefined_instruction:
	blink	#2

.globl software_interrupt
software_interrupt:
	blink	#3

.globl prefetch_abort
prefetch_abort:
	blink	#4

.globl data_abort
data_abort:
	blink	#5

.globl not_used
not_used:
	blink	#6

.globl irq
irq:
	blink	#7

.globl fiq
fiq:
	blink	#8

/* The functions below is platform-dependent. */
led1set:
    stmfd   sp!, {r1, r2, lr}
	ldr		r2, =0x2
	ldr		r1, =0x40E00018 @ GPSR0
	str		r2, [r1]

    ldmfd   sp!, {r1, r2, pc}

led1clear:
	mov		pc, lr

led2set:
	mov		pc, lr

led2clear:
	mov		pc, lr

ledsetup:
	mov		pc, lr

ledflash:
	mov		pc, lr

endless_blink:
	wait		#0x2000000
	b		endless_blink

init_i2c:
    stmfd   sp!, {r0, r1, lr}
    
	/* setup MFPRs for I2C */
	ldr		r0, =0xa841
	ldr		r1, =MFP_I2C_SCL
	str		r0, [r1]
	ldr		r1, =MFP_I2C_SDA
	str		r0, [r1]

	/* enable i2c clock */
	ldr             r1, =0x41340010  /* D0CKEN_B */
	ldr             r0, [r1]
	orr             r0, r0, #0x10      /* enable i2c clock */
	str             r0, [r1]

	/* Initialize I2C Unit */
	ldr		r1, =0x40301680 /* I2C register start address */
	mov		r0, #0x0
	str		r0, [r1, #0x20] /* ISAR */
	mov		r0, #0x4000     /* reset I2C */
	str		r0, [r1, #0x10] /* ICR */
	mov		r0, #0x60 	/* set IUE and SCLE */
	str		r0, [r1, #0x10]
    ldmfd   sp!, {r0, r1, pc}

writei2c:
    stmfd   sp!, {r0 - r8, lr}
	ldr		r3, =0x40301680 /* I2C register start address */

	/* Set slave I2C address */
	str		r4, [r3, #0x8] 	/* IDBR */

	/* send a start condition */
	ldr             r0, [r3, #0x10]
	@mov		r0, #0x9	/* START & TB */
	orr		r0, r0, #0x9    /* START & TB */
	ldr             r2, =0xffffeffd
	and             r0, r0, r2      /* ~(STOP | ALDIE) */
	str		r0, [r3, #0x10] /* ICR */

	/* wait until tx buffer empty */
	mov		r8, #0x80000 	/* time out */
1:
	ldr		r2, [r3, #0x18] /* ISR */
	subs		r8, r8, #1
	moveq		pc, lr
	ands		r2, r2, #0x40	/* ITE in ISR */
	beq		1b
	str		r2, [r3, #0x18] /* clear status */

	/* send first byte(register address) */
	str		r5, [r3, #0x8] 	/* IDBR */
	ldr             r0, [r3, #0x10] /* read ICR */
@	mov		r0, r0, #0x69	/* IUE, SCLE, START & TB */
	ldr             r2, =0x1008
	orr		r0, r0, r2	/* ALDIE, TB */
	ldr             r2, =0xfffffffe
	and		r0, r0, r2	/* ~START */
	ldr             r2, =0xfffffffd
	and             r0, r0, r2          /* ~STOP */
	str		r0, [r3, #0x10]

	/* wait until tx buffer empty */
	mov		r8, #0x80000 	/* time out */
2:
	ldr		r2, [r3, #0x18] /* ISR */
	subs		r8, r8, #1
	moveq		pc, lr
	ands		r2, r2, #0x40	/* ITE in ISR */
	beq		2b
	str		r2, [r3, #0x18] /* clear status */

	/* send second byte (register value) */
	str		r6, [r3, #0x8] 	/* IDBR */
	ldr             r0, [r3, #0x10] /* read ICR */
@	mov		r0, r0, #0x69	/* IUE, SCLE, START & TB */
	ldr             r2, =0x1008
	orr		r0, r0, r2	/* ALDIE, TB */
	ldr             r2, =0xfffffffe
	and		r0, r0, r2	/* ~START */
	orr             r0, r0, #0x2        /* STOP */
	str		r0, [r3, #0x10]

	/* wait until tx buffer empty */
	mov		r8, #0x80000 	/* time out */
3:
	ldr		r2, [r3, #0x18] /* ISR */
	subs		r8, r8, #1
	moveq		pc, lr
	ands		r2, r2, #0x40	/* ITE in ISR */
	beq		3b
	str		r2, [r3, #0x18] /* clear status */

    ldmfd   sp!, {r0 - r8, pc}

xlli_intr_init:
        stmfd   sp!,    {r0, lr}                @ Save r0, & link on stack

        ldr     r0,  =0x0               @ zero out a work register
        mcr     p6, 0, r0, c1, C0, 0    @ Clear ICMR register
        mcr     p6, 0, r0, c7, C0, 0    @ Clear ICMR2 register
        mcr     p6, 0, r0, c2, C0, 0    @ Clear ICLR register
        mcr     p6, 0, r0, c8, C0, 0    @ Clear ICLR2 register
        ldmfd   sp!,    {r0, pc}                @ Return to caller via stack

xlli_freq_change:
        stmfd   sp!,    {r0, r1, lr}            @ save r0, r1, & link on stack
        mrc     p14, 0, r0, c6, c0, 0       @ Get present status (preserve Turbo and Fast Bus bits)
        orr     r0,  r0,  #2                @ Set the F bit
        mcr     p14, 0, r0, c6, c0, 0       @ initiate the frequency change sequence - Wheeeeeeeee!
@
@       If the clock frequency is chaged, the MDREFR Register must be  rewritten, even
@       if it's the same value. This will result in a refresh being performed and the
@       refresh counter being reset to the reset interval.
@
        ldr     r0,  =0x48100000   @ Get memory controller base address
        ldr     r1,  [r0, #0x004]      @ Get the current state of MDREFR
        str     r1,  [r0, #0x004]      @ Re-write this value
        ldmfd   sp!,    {r0, r1, pc}            @ Return to caller via the stack


//about ddr memory initialize routine
#define     xlli_DMEMREGS_PHYSICAL_BASE     0x48100000
#define     xlli_SDRAM_PHYSICAL_BASE       (0x80000000)  @ Physical base address for SDRAM
#define xlli_OSTREGS_PHYSICAL_BASE (0x40A00000)
#define xlli_OSCR0_offset (0x10)
#define xlli_EMPI_value (0xf4000000)
#define xlli_BIT_8      0x00000100
#define xlli_RCOMP_RCRNG_value 0x2
#define xlli_RCOMP_REI 0xc65d4
#define xlli_BIT_24 0x01000000
#define xlli_MDCNFG_DSCE 0x3
#define xlli_RCOMP_SWEVAL 0x80000000
#define xlli_EMPI_offset 0x090
#define xlli_MDCNFG_offset 0x000
#define xlli_RCOMP_offset 0x100
#define xlli_RCOMP_SWEVAL   0x80000000
#define xlli_MDCNFG_value_16         0x8000072F
#define xlli_MDCNFG_value_32         0x8000072B
#define xlli_RCOMP_UPDATE 0x40000000
#define xlli_BIT_24 0x01000000
#define xlli_MDMRS_MSCS0 0x40000000
#define xlli_MDMRS_MDCOND 0x20000000
#define xlli_MDCNFG_DMCEN 0x40000000
#define xlli_DMCISR_offset 0x078
#define xlli_PAD_MA_offset 0x110
#define xlli_PAD_MDMSB_offset 0x114
#define xlli_PAD_MDLSB_offset 0x118
#define xlli_PAD_DMEM_offset 0x11C
#define xlli_PAD_SDCLK_offset 0x120
#define xlli_PAD_SDCS_offset 0x124
#define xlli_PAD_SMEM_offset 0x128
#define xlli_PAD_SCLK_offset 0x12C
#define xlli_CMA_offset 0xFFC
#define xlli_MDMRS_offset 0x040
#define xlli_MDREFR_offset 0x4
#define xlli_MDMRS_value 0x60000033
#define xlli_MDREFR_value 0x00000023

init_memory:
        stmfd   sp!,    {r0-r4, lr}             @ Save r0-r4, & link on stack
        bl      xlli_Dmem_initP1                        @ init DDR-SDRAM (Part 1)
        ldr     r0,     =xlli_DMEMREGS_PHYSICAL_BASE    @ Dynamic Mem Controller base
        bl      xlli_Rcomp                              @ Perform inital Rcomp calculation
        ldr     r0,     =xlli_DMEMREGS_PHYSICAL_BASE    @ Dynamic Mem Controller base
        ldr     r1,     =xlli_SDRAM_PHYSICAL_BASE       @ DDR SDRAM base address
        bl      xlli_Dmem_initP2                        @ init DDR-SDRAM (Part 2)
        ldmfd   sp!,    {r0-r4, pc}             @ Return to caller via the stack

xlli_Dmem_initP1:
        stmfd   sp!,    {r0-r4, lr}             @ Save r0-r4, & link on stack
@
@ STEP 1: Delay at least 100uS to allow internal clocks for SDCLK to stabilize.
@       This is a required hardware reset power-on wait period. XLLI code waits
@       a little bit longer - just to be sure.
@
        ldr     r0,     =xlli_OSTREGS_PHYSICAL_BASE     @ Load OS timer base address
        ldr     r2,     [r0, #xlli_OSCR0_offset]        @ Fetch starting value of OSCR0
        add     r2,     r2,     #0x150                  @ Actually, 0x145 is about 100uS
xlli_d1: ldr     r1,     [r0, #xlli_OSCR0_offset]        @ Fetch current OSCR0 value
        cmp     r1,     r2                              @ Is the timer past the time out value?
        bmi     xlli_d1                                 @ No - Loop until it is

        mov     r2,     #0      @ Force 16-bit DDR for PXA300/310

@
@       Set up EMPI register
@
        ldr     r4,     =xlli_DMEMREGS_PHYSICAL_BASE    @ Dynamic Memory Control Reg Base
        ldr     r3,     =xlli_EMPI_value                @ Get EMPI value
@
@       If processor is an "A" step processor, clear bits 28 and 29
@
        mrc     p15, 0, r0, c0, c0, 0           @ Fetch processor ID register (in r0)
        ands    r0,     r0,     #0x1C           @ Mask out the A/B/C... Stepping bits
        biceq   r3,     r3,     #0x30000000     @ Clear schmitter bits if "A-step" processor
        bicne   r3,     r3,     #0x40000000     @ Otherwise clear bit 30
        str     r3,     [r4, #xlli_EMPI_offset] @ Load the EMPI register
@
@ STEP 2: Prevent Power Down Command by setting CMA[APDD] bit. (A0 Silicon only - code commented out)
@
@        mov     r0,     #xlli_CMA_APDD                  @ Set this bit to prevent power down
@        str     r0,     [r4, #xlli_CMA_offset]          @ Set the bit
@
@ STEP 3: Program MDCNFG then enable the occupied DRAM partitions.
@         MDCNFG[DMCEN] must remain deasserted!
@
        ands    r2,     r2,     #xlli_BIT_8             @ Isolate the Phone Flipped bit
        ldrne   r2,     =xlli_MDCNFG_value_32           @ Get 32 bit config values (DOT)
        ldreq   r2,     =xlli_MDCNFG_value_16           @ Get 16 bit config values (NO_DOT & MHL)
        and     r0,     r2,     #xlli_MDCNFG_DSCE       @ Save the DSCE bits
        ldr     r3,     =0x8000077C                     @ Mask to clear reserved, DMCEN & DSCE bits
        and     r2,     r2,     r3                      @ Clear reserved, DMCEN & DSCE bits
        str     r2,     [r4, #xlli_MDCNFG_offset]       @ Write to controller
        ldr     r2,     [r4, #xlli_MDCNFG_offset]       @ Insure write is complete
        orr     r2,     r2,     r0                      @ Enable selected partition(s)
        str     r2,     [r4, #xlli_MDCNFG_offset]       @ Write to controller
        ldr     r2,     [r4, #xlli_MDCNFG_offset]       @ Insure write is complete
@
@ Step 4: Wait a typical NOP power-up waiting period required by SDRAM (Typically
@         200 uS) Steps 5 & 6 can be executited during this waiting period. However,
@         xlli will do the wait now and then perform steps 5 & 6.
@
        ldr     r0,     =xlli_OSTREGS_PHYSICAL_BASE     @ Load OS timer base address
        ldr     r2,     [r0, #xlli_OSCR0_offset]        @ Fetch starting value of OSCR0
        add     r2,     r2,     #0x2A0                  @ Actually, 0x28A is about 200uS
xlli_m2: ldr     r1,     [r0, #xlli_OSCR0_offset]        @ Fetch current OSCR0 value
        cmp     r1,     r2                              @ Is the timer past the time out value?
        bmi     xlli_m2                                 @ No - Loop until it is
@
@       In early silicon it has been observed that the Rcomp eval may not complete
@       although the SWEVAL bit was set. The edits below will set this bit up to
@       four times before exiting.
@
        mov     r1,     #4              @ Set up Rcomp retry loop count
@
@ STEP 5: Perform Resistive Compensation calibration cycle (RCOMP)
@
xlli_m3: ldr     r3,     =((xlli_RCOMP_RCRNG_value<<25) | xlli_RCOMP_REI | xlli_BIT_24)
        str     r3,     [r4, #xlli_RCOMP_offset]        @ Write inital values to RCOMP
        orr     r3,     r3,     #xlli_RCOMP_SWEVAL      @ Set bit to perform the RCOMP
        str     r3,     [r4, #xlli_RCOMP_offset]        @ Start the RCOMP evaluation
@
@       Wait for Rcomp evaluation cycle to complete - RCOMP[SWEVAL] clears.
@       A "time out" counter is in this loop - just in case the eval bit doesn't clear
@       so there is an exit path.
@
         mov     r0,     #0x10000                        @ Set up arbitrary time out counter
xlli_m4: ldr     r2,     [r4, #xlli_RCOMP_offset]        @ Fetch status
        subs    r0,     r0,     #1                      @ Decrement loop count
        beq     xlli_m9                                 @ Branch if timed out
        ands    r2,     r2,     #xlli_RCOMP_SWEVAL      @ Is the evaluation complete?
        bne     xlli_m4                                 @ No - loop until done.

xlli_m8: ldmfd   sp!,    {r0-r4, pc}             @ Return to caller via the stack

xlli_m9: subs    r1,     r1,     #1      @ Decrement re-try count
        bne     xlli_m3                 @ Try again if count is not zero
        b       xlli_m8                 @ Otherwise - time to give up on this!!!



@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@
@*******************************************************************************
@
@ ********************************
@ **********            **********
@ ********** xlli_Rcomp **********   PERFORM RCOMP CALCULATION (UPDATE PAD REGISTERS)
@ **********            **********
@ ********************************
@
@ Performs Rcomp calculations based on PCODE and NCODE values presently in
@ the DMCISR register.
@
@       INPUT PARAMETER REQUIRED:
@
@         R0 = Dynamic Memory Control Reg Base Address
@           If bit 0 of this address is set, the drive impedance of the clock signal
@           is reduced from 40 to 25 ohms in order to drive up to four devices.
@
@       RETURNED STATUS/VALUE(s):
@
@         none
@
@
xlli_Rcomp:
        stmfd   sp!,    {r0-r9, lr}             @ Save r0-r7, & link on stack

        and     r9,     r0,     #1      @ Save bit 0 status in r9
        bic     r4,     r0,     #3      @ Save base address in r4 with bits 1:0 cleared
@
@       Obtain the NCODE and PCODE values from the DMCISR register and use this
@       starting value to calculate the final NSLEW and PSLEW values to be
@       programmed into each pad group.
@
        ldr     r2,     [r4, #xlli_DMCISR_offset]       @ Get results of the evaluation
        and     r6,     r2,     #0x003F8000             @ Extract NCODE
        mov     r6,     r6,     LSR #15                 @ and right justify
        and     r5,     r2,     #0x1FC00000             @ Extract PCODE
        mov     r5,     r5,     LSR #22                 @ and right justify
@
@       At this point: r5 = PCODE, r6 = NCODE
@
@       For the PXA300 and PXA320 processors the PSLEW and NSLEW values
@       are calculated as follows:
@
@       PSLEW = -2.96 - 0.0435(PCODE) + 0.348(NCODE)
@       NSLEW = 2.88 + 0.250(PCODE) - 0.125(NCODE)
@       PCODE and NCODE for the PAD_SDCLK registers is multiplied by 40/25
@
@       For the PXA310 processor, the PSLEW and NSLEW formulas are calculated
@       as follows:
@
@       PSLEW =  3.9114 - 0.1172(PCODE) + 0.1964(NCODE)
@       NSLEW = -9.4340 + 0.3564(PCODE) + 0.1906(NCODE)
@
@       To gain the desired accuracy, the constant values above are multiplied
@       by 10,000 (so 0.0435 will become 435) and the math is done as integer math
@       rather than floating point. When the calculations are done, the result
@       is divided by 10,000 and rounded up/down to the closest integer value
@       for PSLEW and NSLEW.
@
@
@       Do the PSLEW calculation. 5000 is added to PSLEW before it is divided
@       by 10,000 so the result is rounded to the closest whole number.
@
        ldr     r3,     =1964           @ load 0.1964 * 10,000 into r3
        mul     r2,     r3,     r6      @ r2 = 1964(NCODE)
        ldr     r3,     =1172           @ load 0.1172 * 10,000 into r3
        mul     r1,     r3,     r5      @ r1 = 1172(PCODE)
        sub     r2,     r2,     r1      @ subtract the PCODE from the NCODE
        ldr     r3,     =(39114 + 5000) @ load 3.9114 * 10,000 + 5000 into r3
        add     r0,     r2,     r3      @ r0 = PSLEW * 10,000 at this point
        ldr     r1,     =10000          @ Set up divisor
        bl      xlli_U32Divide          @ Divide!
        mov     r7,     r2              @ Save the calculated PSLEW value in r7
@
@       Do the NSLEW calculation. 5000 is added to NSLEW before it is divided
@       by 10,000 so the result is rounded to the closest whole number.
@
        ldr     r3,     =3564           @ load 0.3564 * 10,000 into r3
        mul     r1,     r3,     r5      @ r1 = 3564(PCODE)
        ldr     r3,     =1906           @ load 0.1906 * 10,000 into r3
        mul     r2,     r3,     r6      @ r2 = 1906(NCODE)
        add     r1,     r1,     r2      @ add the NCODE and the PCODE
        ldr     r3,     =(94340 + 5000) @ load 9.4340 * 10,000 + 5000 into r3
        sub     r0,     r1,     r3      @ r0 = NSLEW * 10,000 at this point
        ldr     r1,     =10000          @ Set up divisor
        bl      xlli_U32Divide          @ Divide!
        mov     r8,     r2              @ Save the calculated NSLEW value in r8

@
@       At this point the registers we care about contain the following:
@
@               r4 = Dynamic Memory Control Reg Base Address
@               r5 = PCODE value
@               r6 = NCODE value
@               r7 = PSLEW value
@               r8 = NSLEW value
@
@       Next step is to combine the NCODE, PCODE, NSLEW, PSLEW values and program
@       all the pad registers to the same value.
@
        mov     r5,     r5,     LSL #24 @ Move the PCODE into position (LSB to bit 24)
        mov     r6,     r6,     LSL #16 @ Move the NCODE into position (LSB to bit 16)
        mov     r7,     r7,     LSL #8  @ Move the PSLEW into position (LSB to bit 8)
        orr     r8,     r8,     r7      @ OR the NSLEW and PSLEW values together
        orr     r8,     r8,     r6      @ OR in the NCODE value
        orr     r8,     r8,     r5      @ OR in the PCODE value
@
@       The PAD register data has been assembled into r3. Now the code needs
@       to send this data to the selected PAD registers.
@
        str     r8,     [r4, #xlli_PAD_MA_offset]       @ Load the pad registers
        str     r8,     [r4, #xlli_PAD_MDMSB_offset]    @
        str     r8,     [r4, #xlli_PAD_MDLSB_offset]    @
        str     r8,     [r4, #xlli_PAD_DMEM_offset]     @
        str     r8,     [r4, #xlli_PAD_SDCS_offset]     @
        str     r8,     [r4, #xlli_PAD_SMEM_offset]     @
        str     r8,     [r4, #xlli_PAD_SCLK_offset]     @
@
@       Check to see if the clock drive strength is to be set for 40 or 25 ohms.
@       If this routine was called with bit 0 in the memory controller base address
@       set, change the clock drive impedance to 25 Ohms. (This is checked by seeing
@       if bit 0 in r9 is set or cleared).
@
        cmp     r9,     #0      @ Change clock drive impedance to 25 Ohms?
        beq     xlli_r5         @ No - skip over the following code
@
@       Adjust PCODE value for PAD_SDCLK register by 40/25
@
        mov     r3,     r8,     LSR #24 @ Get PCODE
        and     r3,     r3,     #0x7F   @ Mask out any other data
        mov     r2,     #40             @ Multiplier
        mul     r0,     r2,     r3      @ Multiply PCODE
        add     r0,     r0,     #12     @ Add 1/2 divisor value to take care of result rounding
        mov     r1,     #25             @ load divisor
        bl      xlli_U32Divide          @ Divide!
        mov     r2,     r2,     LSL #24         @ Move to PCODE position in register
        bic     r8,     r8,     #0x7F000000     @ Clear old PCODE bits
        orr     r8,     r8,     r2              @ OR in new PCODE bits
@
@       Adjust NCODE value for PAD_SDCLK register by 40/25
@
        mov     r3,     r8,     LSR #16 @ Get NCODE
        and     r3,     r3,     #0x7F   @ Mask out any other data
        mov     r2,     #40             @ Multiplier
        mul     r0,     r2,     r3      @ Multiply NCODE
        add     r0,     r0,     #12     @ Add 1/2 divisor value to take care of result rounding
        mov     r1,     #25             @ load divisor
        bl      xlli_U32Divide          @ Divide!
        mov     r2,     r2,     LSL #16         @ Move to NCODE position in register
        bic     r8,     r8,     #0x7F0000       @ Clear old NCODE bits
        orr     r8,     r8,     r2              @ OR in new NCODE bits

xlli_r5: str     r8,     [r4, #xlli_PAD_SDCLK_offset]    @ Write the PAD_SDCLK register
@
@       Set the UPDATE bit in the Rcomp register & insure bit 24 is set
@
        ldr     r3,     [r4, #xlli_RCOMP_offset]                        @ Fetch RCOMP register value
        orr     r3,     r3,     #(xlli_RCOMP_UPDATE | xlli_BIT_24)     @ Set the UPDATE bit
        str     r3,     [r4, #xlli_RCOMP_offset]                        @ Write RCOMP register back
@
@       All done - return to caller
@

        ldmfd   sp!,    {r0-r9, pc}             @ Return to caller via the stack

@
@*******************************************************************************
@
@ **************************************
@ **********                  **********
@ ********** xlli_Dmem_initP2 **********   INITALIZE DYNAMIC MEMORY CONTROLLER
@ **********                  **********           (PART 2 OF 2 PARTS)
@ **************************************
@
@ The sequence below is based on the recommended memory initializing procedure
@ recommended in the PXA3xx External Memory Controller Manual
@ (Issue 16860 - Section 1.4.3.6)
@
@       INPUT PARAMETER REQUIRED:
@
@               R0 = Dynamic Memory Control Reg Base Address
@
@       RETURNED STATUS/VALUE(s):
@
@               NONE
@

xlli_Dmem_initP2:
        stmfd   sp!,    {r0-r2, lr}             @ Save r0-r2, & link on stack
@
@ STEP 7: Write the Mode Register Set Config Register (MDMRS)
@
        ldr     r0,     =xlli_DMEMREGS_PHYSICAL_BASE    @ Dynamic Memory Control Reg Base
        ldr     r2,     =xlli_MDMRS_value               @ Get config values
        orr     r2,     r2,     #(xlli_MDMRS_MSCS0 | xlli_MDMRS_MDCOND)      @ Set the "Condition DRAM" bit
        str     r2,     [r0, #xlli_MDMRS_offset]        @ Update register
        ldr     r2,     [r0, #xlli_MDMRS_offset]        @ Insure write is complete
@
@ STEP 8: Program the MDREFR with the desired refresh interval
@
        ldr     r1,     =xlli_MDREFR_value              @ Fetch inital refresh value
        str     r1,     [r0, #xlli_MDREFR_offset]       @ Write to register
        ldr     r1,     [r0, #xlli_MDREFR_offset]       @ Insure the write completes                           @ No - Loop until time runs out                              @ No - loop until they are
@
@ STEP 9: Enable the dynamic memory controller
@
        ldr     r1,     [r0, #xlli_MDCNFG_offset]       @ Get the MDCNFG value
        orr     r1,     r1,     #xlli_MDCNFG_DMCEN      @ Set the enable bit
        str     r1,     [r0, #xlli_MDCNFG_offset]       @ Write to controller
        ldr     r1,     [r0, #xlli_MDCNFG_offset]       @ Insure write is complete
@
@ STEP 10: Optionally Enable auto-power-down by clearing the CMA[APDD] bit
@
        mov     r2,     #0                      @ Clear a work register
        str     r2,     [r0, #xlli_CMA_offset]  @ Clear the bit
@
@       SDRAM should be ready for use at this point - return to caller
@
        ldmfd   sp!,    {r0-r2, pc}             @ return to caller via the stack



@@@@@@@@@@@@@@@@@@@@@@@@@@
@
@*******************************************************************************
@
@       ******************
@       *                *
@       * xlli_U32Divide * Subroutine
@       *                *
@       ******************
@
@ This subroutine performs an unsigned 32 bit divide
@
@ PARAMETER PASSING:
@
@ r0  contains the dividend (preserved)
@ r1  contains the divisor  (preserved)
@ r2  will contain the result on subroutine exit
@ r3  will contain the remainder on subroutine exit
@
xlli_U32Divide:
        stmfd   sp!,    {r0, r1, r4, r5, lr}    @ Save used registers

        mov     r2,     #0      @ Clear result
        mov     r3,     #0      @ Clear remainder
        mov     r5,     #32     @ load r5 with loop count
        mrs     r4,     CPSR                    @ Get CPSR
        bic     r4,     r4,     #0x20000000     @ Clear carry bit
@
@       Main division loop begins here
@
xlli_D1: msr     CPSR_f,         r4      @ write flags back
        adcs    r2,     r2,     r2      @ Shift C bit into result
        adcs    r0,     r0,     r0      @ Rotate r0 left 1 bit through C bit
        adcs    r3,     r3,     r3      @ Rotate data left, into r3
        subs    r3,     r3,     r1      @ Subtract dividend
        blt     xlli_D2                 @ Branch if negative
        mrs     r4,     CPSR            @ Get CPSR
        orr     r4,     r4,     #0x20000000     @ Set carry bit
        msr     CPSR_f,         r4      @ write flags back
        b       xlli_D3                 @ Check loop count

xlli_D2: add     r3,     r3,     r1      @ Restore r3
        mrs     r4,     CPSR            @ Get CPSR
        bic     r4,     r4,     #0x20000000     @ Clear carry bit
        msr     CPSR_f,         r4      @ write flags back

xlli_D3: mrs     r4,     CPSR            @ Get CPSR
        subs    r5,     r5,     #1      @ decrement loop count
        bne     xlli_D1                 @ Loop until done
@
@       Shift the last bit into the result and return to caller
@
        msr     CPSR_f,         r4      @ write flags back
        adcs    r2,     r2,     r2      @ Shift C bit into result
        adcs    r0,     r0,     r0      @ Rotate r0 left 1 bit through C bit

        ldmfd   sp!,    {r0, r1, r4, r5, pc}    @ Return to caller via the stack


.align 4
bss_start:	.word	___bss_start
bss_end	:	.word	___bss_end
